
`ifndef MEMORY_SCRATCH_V_
`define MEMORY_SCRATCH_V_

`define PACOBLAZE3M
`include "/home/snepomny/VLSI/paco/pacoblaze-2.2/pacoblaze/pacoblaze_inc.v"

`timescale 1ps/1ps
//======================================================================================================
`timescale 1ps/1ps
//======================================================================================================
module BF_Switch(
		In_Packet_up,		
		In_Packet_down,
		Out_Packet_up,
		Out_Packet_down
);

	parameter read_bit = 0;
	parameter D = 2;  // DataSize
   parameter offset_bits = 2;
	parameter add_offset = 2+D;
	parameter levels = 1;   
	parameter Port_Num = 2**levels;
	parameter MemoryModule_Num = Port_Num;
	parameter M = levels; //  = log( MemoryModule_Num )
   parameter Packet_Size = 2+D+M+offset_bits+M;

	input 		[Packet_Size-1:0]	 	In_Packet_up;
	input		   [Packet_Size-1:0]		In_Packet_down;
	output 		[Packet_Size-1:0]	 	Out_Packet_up;
	output		[Packet_Size-1:0]		Out_Packet_down;
	reg 		   [Packet_Size-1:0]	 	Out_Packet_up;
	reg			[Packet_Size-1:0]		Out_Packet_down;

	wire		[3:0] 			all_logic;

	assign all_logic[3] = In_Packet_up[0];
	assign all_logic[2] = In_Packet_down[0];
	assign all_logic[1] = In_Packet_up[add_offset+read_bit];   // Destination (bit) of UP port.   0-Up requested, 1-Down requested.
	assign all_logic[0] = In_Packet_down[add_offset+read_bit]; // Destination (bit) of DOWN port. 0-Up requested, 1-Down requested.
	
	always @* begin 
			//$display("sw:: %d (%b) ",all_logic, all_logic);
			case (all_logic)
				4'b0000, 4'b0001, 4'b0010, 4'b0011: begin //stop all
					Out_Packet_up = 0;
					Out_Packet_down = 0;
			   end
				4'b0100, 4'b0110: begin //down is alive and wants to go up
					Out_Packet_down = 0;
					Out_Packet_up = In_Packet_down;
			   end
				4'b0101, 4'b0111: begin //down is alive and wants to go down
					Out_Packet_up = 0;
					Out_Packet_down = In_Packet_down;
			   end
				4'b1000, 4'b1001: begin //up is alive and wants to go up
					Out_Packet_up = In_Packet_up;
					Out_Packet_down = 0;
			   end
				4'b1010, 4'b1011: begin //up is alive and wants to go down
					Out_Packet_up = 0;
					Out_Packet_down = In_Packet_up;
			   end
				4'b1100: begin //both alive, collide on up, up wins
					Out_Packet_up = In_Packet_up;
					Out_Packet_down = 0;
			   end
				4'b1101: begin //both alive, straight
					Out_Packet_up = In_Packet_up;
					Out_Packet_down = In_Packet_down;
			   end		
				4'b1110: begin //both alive, cross
					Out_Packet_up = In_Packet_down;
					Out_Packet_down = In_Packet_up; 
			   end	
				4'b1111: begin //both alive, collide on down, down wins
					Out_Packet_up = 0;
					Out_Packet_down = In_Packet_down;
			   end					
				default: begin 
					Out_Packet_down = 0;// send the Zero-Packet. 
					Out_Packet_up = 0;  // send the Zero-Packet. 
					end
			endcase 		
	end
			
	
endmodule     
//======================================================================================================
module BF_Network( 
		In_AllPackets_r,
		Out_AllPackets
	 );

	parameter levels = 1;   
	parameter D = 2;  // DataSize
   parameter offset_bits = 2;
	parameter add_offset = 0;
	parameter Port_Num = 2**levels;
	parameter MemoryModule_Num = Port_Num;
	parameter M = levels; //  = log( MemoryModule_Num )
   parameter Packet_Size = 2+D+M+offset_bits+M;

	input 	[(Packet_Size * Port_Num) - 1:0]			In_AllPackets_r;
	wire 	   [(Packet_Size * Port_Num) - 1:0]			In_AllPackets;
	output 	[(Packet_Size * MemoryModule_Num) - 1:0]	Out_AllPackets;
	
													//Levels     // Port_Num    // In-Out					 
	wire [Packet_Size - 1:0] WireMatrix [levels-1:0] [Port_Num-1:0] [1:0]; //???

	parameter i = 0;
	genvar level,port;
	
		generate
		for (port = 0; port < Port_Num; port = port+1)
		begin: x
			assign In_AllPackets[((1+port)*Packet_Size)-1:port*Packet_Size] = 
			       { port[M-1:0], In_AllPackets_r[((1+port)*Packet_Size)-1-M:port*Packet_Size]};
		end
	endgenerate 
 
   generate 
		for (level = levels-1; level >= 0; level = level-1)
		begin: a
			for (port = 0; port < Port_Num; port = port+2)
			begin: b	
				BF_Switch #(level, D, offset_bits, add_offset, levels) sw ( 
				WireMatrix[level][port+1][0], 
				WireMatrix[level][port][0], 
				WireMatrix[level][port+1][1],
				WireMatrix[level][port][1]
				);
			end
		end
	endgenerate 		

	generate
		for (port = 0; port < Port_Num; port = port+1)
		begin: c
			assign WireMatrix[levels-1][port][0] = 
			       In_AllPackets[((port+1)*Packet_Size)-1:(port*Packet_Size)];
			assign Out_AllPackets[((port+1)*Packet_Size)-1:(port*Packet_Size)] =
			       WireMatrix[0][port][1];
		end
	endgenerate

	genvar band, offset1, offset2;
	generate
		for (level = levels-1; level >= 1; level = level - 1 ) 
		begin: d
			// determine amount of Equator bands:
			// Num of Equator bands is 1 << antiLevel;
			// Size of Equator band is 1 << level;
			parameter antiLevel = levels - level -1;

			parameter AmountOfBands    = 2 ** antiLevel;
			parameter SizeOfEqBand     = 2 ** (level + 1);
			parameter SizeOfHalfEqBand = SizeOfEqBand / 2;
		
			for (band = 0; band < AmountOfBands; band = band + 1)
			begin: e
			
			   // color 0
				for (offset1 = 0; offset1 < SizeOfHalfEqBand; offset1 = offset1 + 2)
				begin: color0
					parameter beginOffset = band * SizeOfEqBand;
					// Up   - straight forward (Higher Entrance)
					assign WireMatrix[level-1][beginOffset+offset1][0] =  
					       WireMatrix[level  ][beginOffset+offset1][1];

					// Down - Cross Equator line (Higher Entrance)
					assign WireMatrix[level-1][beginOffset+offset1+SizeOfHalfEqBand][0] =  
					       WireMatrix[level  ][beginOffset+offset1+1][1];
				end

			   // color 1
				for (offset2 = 0; offset2 < SizeOfHalfEqBand; offset2 = offset2 + 2)
				begin: color1
					parameter beginOffset = band * SizeOfEqBand + SizeOfHalfEqBand;
					// Up   - Cross Equator line (Lower entrance)
					assign WireMatrix[level-1][beginOffset+offset2-SizeOfHalfEqBand+1][0] =  
					       WireMatrix[level  ][beginOffset+offset2][1];

					// Down - straight forward   (Lower entrance)
					assign WireMatrix[level-1][beginOffset+offset2+1][0] =  
					       WireMatrix[level  ][beginOffset+offset2+1][1];
				end
			end		
		end
	endgenerate
endmodule
//======================================================================================================

module MemoryModule(
		In_Packet,
		Out_Packet,
		clk );

	parameter D = 8; // Datasize
	parameter L = 8; // Log2 (Linenum)
	parameter M = 4; // Log2 (MemoryModule_num)
	parameter Line_Num = 2**L;

	//packet related info:
	parameter Packet_Size = 2+D+M+L+M;

	input [Packet_Size-1:0]	In_Packet;
	output[Packet_Size-1:0]	Out_Packet;
	
	//reg [Packet_Size-1:0]	In_Packet;
	reg[Packet_Size-1:0]	Out_Packet;
	reg [D-1:0]	MM_Data [Line_Num-1:0];

   input clk;
			
	always @ (posedge clk)
	begin a:
		if (In_Packet[0] == 0) begin
			Out_Packet <= 0;
		end
		else begin
			if (In_Packet[1] == 0) begin //Read Mode
				Out_Packet[ 1:0 ] <= In_Packet[ 1:0 ];
				Out_Packet[ D+1:2 ] <= MM_Data[ In_Packet[2+D+M+L-1:2+D+M] ];
				Out_Packet[ Packet_Size-1:2+D ] <= In_Packet[ Packet_Size-1:2+D ];
		end
			else begin // Write Mode
				//MM_Data[Intr_Add] = Data;
				//Out_P = {Pr_Add,Intr_Add,RW_flag,Data,MM_Add};
				MM_Data[ In_Packet[2+D+M+L-1:2+D+M] ] <= 
					In_Packet[ 2+D-1:2 ];
				Out_Packet <= In_Packet;
				//Out_Packet <= 2'b11;
			end
		end
	end		
endmodule
//======================================================================================================

module AllMemoryModule(
		In_AllPackets,
		Out_AllPackets,
		clk
	 );

	parameter D = 8;  // DataSize
	parameter L = 8; // offset bits
	parameter M = 2; //  = log( MemoryModule_Num )
	parameter levels = M;   

   parameter offset = 2**L;
	parameter Port_Num = 2**levels;
	parameter MemoryModule_Num = 2**M;
	
   parameter Packet_Size = 2+D+M+L+M;

	input 	[(Packet_Size * Port_Num) - 1:0]			In_AllPackets;
	input clk;
	output 	[(Packet_Size * MemoryModule_Num) - 1:0]	Out_AllPackets;

	genvar mmodule;
	generate
	for (mmodule=0; mmodule<MemoryModule_Num; mmodule = mmodule + 1)
		begin: MMs
			MemoryModule #(D,L,M) sw (
				In_AllPackets[((1+mmodule)*Packet_Size)-1:(mmodule)*Packet_Size],
				Out_AllPackets[((1+mmodule)*Packet_Size)-1:(mmodule)*Packet_Size],
				clk
			);
		end

	endgenerate 
endmodule

module sharedMem(in,out,clk);

	// Packet-related information:	
	parameter D = 4;  // DataSize
   parameter offset_bits = 4; // size of the offset (aka line num)
	parameter add_offset = 2+D; // the place in the packet where the of offset is at.
	parameter levels = 1; // (aka M)
	parameter M = levels; //  = log( MemoryModule_Num )
	parameter Port_Num = 2**levels;  // (aka memory module num)
	parameter MemoryModule_Num = Port_Num;

	// Total packet size:
   parameter Packet_Size = 2+D+M+offset_bits+M; 
	// Last (MSB) M is Return Address.


  	input 	[(Packet_Size * Port_Num) - 1:0]			in;
	input clk;
	wire 	[(Packet_Size * Port_Num) - 1:0]			in;
	output 	[(Packet_Size * MemoryModule_Num) - 1:0]	out;
	
 	wire 	[(Packet_Size * MemoryModule_Num) - 1:0]	BF1_to_Mem;
	wire 	[(Packet_Size * MemoryModule_Num) - 1:0]	Mem_to_BF2;

	// BF #1:
   BF_Network #(.levels(levels), .D(D), .offset_bits(offset_bits), .add_offset(add_offset))
  	bfn1(in, BF1_to_Mem);  
	
	// RAM:
	AllMemoryModule #(D,offset_bits,M) mm(
			BF1_to_Mem,
			Mem_to_BF2,
			clk
	 );

	// BF #2:
    //note: we are adding_offset here - the second net should read return address rather than the main one
	 BF_Network #(.levels(levels), .D(D), .offset_bits(offset_bits),  .add_offset(add_offset+M+offset_bits))
	 bfn2 (Mem_to_BF2, out);

endmodule

module manager(address,ebit,wbit,data_in,data_out,g,clk);
// Packet-related information:	
	parameter D = 4;  // DataSize
   parameter offset_bits = 4; // size of the offset (aka line num)
	parameter add_offset = 2+D; // the place in the packet where the of offset is at.
	parameter levels = 1; // (aka M)
	parameter M = levels; //  = log( MemoryModule_Num )
	parameter Port_Num = 2**levels;  // (aka memory module num)
	parameter MemoryModule_Num = Port_Num;

	// Total packet size:
   parameter Packet_Size = 2+D+M+offset_bits+M; 
	// Last (MSB) M is Return Address.
	input [offset_bits+M-1:0] address;
	input wbit;
	input ebit;
	input [D - 1:0] data_in;
	
	input clk;
	
	wire 	[(Packet_Size * MemoryModule_Num) - 1:0] oshm;
	output [D - 1:0] data_out;
   wire [D - 1:0] data_out;
	output wire 	[(Packet_Size * MemoryModule_Num) - 1 :0] g;

	sharedMem sh({0,address,data_in,wbit,ebit},oshm,clk);
	
	assign data_out = oshm[D+1:2];
	assign g = oshm;

endmodule


module pair_manager(
	address1,ebit1,wbit1,data_in1,data_out1,
	address2,ebit2,wbit2,data_in2,data_out2,
	debugIn,shmOut,clk
);
// Packet-related information:	
	parameter D = 4;  // DataSize
   parameter offset_bits = 4; // size of the offset (aka line num)
	parameter add_offset = 2+D; // the place in the packet where the of offset is at.
	parameter levels = 1; // (aka M)
	parameter M = levels; //  = log( MemoryModule_Num )
	parameter Port_Num = 2**levels;  // (aka memory module num)
	parameter MemoryModule_Num = Port_Num;

	// Total packet size:
   parameter Packet_Size = 2+D+M+offset_bits+M; 
	// Last (MSB) M is Return Address.
	input [offset_bits+M-1:0] address1;
	input wbit1;
	input ebit1;
	input [D - 1:0] data_in1;
	input [offset_bits+M-1:0] address2;
	input wbit2;
	input ebit2;
	input [D - 1:0] data_in2;	
	input clk;
	
	wire 	[(Packet_Size * MemoryModule_Num) - 1:0] oshm;
	output [D - 1:0] data_out1;
   wire [D - 1:0] data_out1;
	output [D - 1:0] data_out2;
   wire [D - 1:0] data_out2;
	output wire [(Packet_Size * MemoryModule_Num) - 1 :0] shmOut;
	output wire [(Packet_Size * MemoryModule_Num) - 1 :0] debugIn;

	sharedMem #(
		.D(`scratch_width), 
		.levels(1), 
		.offset_bits(`scratch_depth)
	) sh 
	(
		.in ({0,
				address2,
				data_in2,
				wbit2,
				ebit2,
				0,
				address1,
				data_in1,
				wbit1,
				ebit1}),
		.out(oshm),
		.clk(clk)
	);
	
	assign data_out1 = oshm[D+1:2];
	assign outShm = oshm;
	assign debugIn = {0,
				address2,
				data_in2,
				wbit2,
				ebit2,
				0,
				address1,
				data_in1,
				wbit1,
				ebit1};

endmodule

/*
module memory_scratch(
	address, write_enable, data_in, data_out,
	reset, clk
);
input clk, reset, write_enable;
input [`scratch_depth-1:0] address;
input [`scratch_width-1:0] data_in;
output [`scratch_width-1:0] data_out;

reg [`scratch_width-1:0] spr[0:`scratch_size-1];

assign data_out = spr[address];
always @(posedge clk) if (write_enable) spr[address] <= data_in;

endmodule
*/

module memory_scratch(
	address1, ram_en1, write_enable1, data_in1, data_out1,
	address2, ram_en2, write_enable2, data_in2, data_out2,
	reset, clk,
	debugIn, debugOut
);
parameter Packet_Size = 2 + `scratch_width+1+`scratch_depth;
input clk, reset, ram_en1, ram_en2, write_enable1, write_enable2;
input [`scratch_depth-1:0] address1;
input [`scratch_width-1:0] data_in1;
output [`scratch_width-1:0] data_out1;

input [`scratch_depth-1:0] address2;
input [`scratch_width-1:0] data_in2;
output [`scratch_width-1:0] data_out2;
output [(Packet_Size * 2) - 1:0] debugIn;
output [(Packet_Size * 2) - 1:0] debugOut;


	pair_manager #(
		.D(`scratch_width), 
		.levels(1), 
		.offset_bits(`scratch_depth-1)
	)
	shm (
		.address1(address1),
		.ebit1(ram_en1),
		.wbit1(write_enable1),
		.data_in1(data_in1),
		.data_out1(data_out1),	
		.address2(address1),
		.ebit2(0),
		.wbit2(write_enable1),
		.data_in2(data_in1),
		.data_out2(data_out1),
		.shmOut(debugOut),
		.debugIn(debugIn),
		.clk(clk)
	);
	

endmodule


`endif // PACOBLAZE_SCRATCH_V_
